feat(cli): engram reindex <file> + optional --auto-reindex hook (#8)#13
Merged
NickCirv merged 1 commit intoNickCirv:mainfrom Apr 21, 2026
Merged
Conversation
…Cirv#8) Closes NickCirv#8. Two parts, one commit; the maintainer offered to split into two PRs in the acceptance comment — happy to split on request. Part 1 — CLI subcommand - `engram reindex <file> [-p, --project <path>] [--verbose]`: re-indexes a single file via the shared `syncFile()` primitive from NickCirv#9/NickCirv#12. Semantics match `engram watch`: exists → reindex; missing-but- previously-indexed → prune; unsupported ext or ignored dir → silent exit 0 (safe to fire from a PostToolUse hook on every edit). - On success: single line `engram: reindexed <file> (<N> nodes)` (or `pruned`) using locale-stable `formatThousands` — no Windows ICU flake, matching the e099bf1 migration. - Missing graph: exits 1 with `engram: no graph found at <root>. Run 'engram init' first.`, mirroring `watchProject`'s explicit error. - Errors: single stderr line; `--verbose` surfaces the stack trace. - New pure formatter `formatReindexLine(result, displayPath)` exported from `src/watcher.ts` (returns null for skipped so callers stay silent). Part 2 — optional auto-reindex wiring - `engram reindex-hook`: PostToolUse stdin entry point. Reads Claude Code's JSON payload, extracts `tool_input.file_path`, delegates to `runReindexHook()` → `syncFile()`. ALWAYS exits 0 — malformed JSON, missing fields, non-project cwd, and internal errors all resolve to a silent no-op. Same bounded-stdin + watchdog pattern as `intercept`; `process.exitCode = 0` without `process.exit` so sql.js WASM drains. - `engram install-hook --auto-reindex`: off by default so existing users aren't surprised. Appends a second PostToolUse entry with matcher `Edit|Write|MultiEdit` (per the maintainer's acceptance) whose command is `engram reindex-hook`. Idempotent. - `isEngramHookEntry()` broadened to also match `engram reindex-hook` so `engram uninstall-hook` strips the auto-reindex entry alongside the primary intercept entries. - Installer idempotence check targets `engram intercept` specifically (via a new internal `entryContainsCommand` helper) — otherwise the newly-broadened `isEngramHookEntry` would false-positive when only the reindex-hook entry is present, causing install to skip adding the missing intercept entry. Implementation notes - `runReindexHook` walks up from the FILE path (not `cwd`) to find the project root. Claude Code's session `cwd` often sits above the engram-initialized project that owns the edited file (multi-project parent, monorepo subtree); `findProjectRoot(cwd)` would silently no-op in that case. Test covers this exact shape. - MultiEdit included in the matcher per the maintainer's acceptance note (the issue originally said Edit|Write). Tests (+16 new, all green — 704 total) - `formatReindexLine`: 4 unit tests (indexed/pruned/skipped/locale-stable). - `engram reindex` E2E via subprocess: happy path, non-code silent, no-graph error, modified-file-updates-graph (old fn out / new fn in), two-process concurrency (AC 6, honestly labelled as multi-process). - `runReindexHook`: 5 unit tests (happy, relative path, malformed-shape matrix, cwd-outside-project, walks-up-from-file-not-cwd). - `engram reindex-hook` E2E via subprocess: happy + malformed JSON + empty stdin, always exits 0. - `installEngramHooks({ autoReindex: true })`: 6 tests (adds second PostToolUse entry, off-by-default, idempotent, add-onto-existing, uninstall-strips-it, uninstall-preserves-non-engram). - `buildReindexHookEntry`, broadened `isEngramHookEntry`, and `formatInstallDiff` coverage for the new entry. Live smoke test (my machine) - `engram install-hook --scope user --auto-reindex` wrote the entry cleanly with a backup. - A `Write` of `src/proof-issue-8.ts` fired `engram reindex-hook` via Claude Code; `engram query` found the new function immediately. - `engram reindex src/proof-issue-8.ts` after `rm` produced `engram: pruned src/proof-issue-8.ts (2 nodes)` and the query came back empty. - `engram uninstall-hook` removed both engram entries cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merged
5 tasks
NickCirv
added a commit
that referenced
this pull request
Apr 21, 2026
Captures the brainstorming session outcome for the three-release plan: - v2.1 "Reliability + Zero-Friction Install" — close the bleeding, merge contributor PRs (#6, #13), fix #11, ship engram update / engram doctor / engram setup, close #14 via Bash PostTool parser. - v2.2 "Spine" — integrate Serena as an engram provider via a new reusable MCP-client subsystem. Engram becomes the orchestrator, not a fighter of semantic-search tools. - v3.0 "Landmines" — mistakes-as-moat expansion + R2 repositioning ("the context tool that remembers what broke"). Keep the name, rebrand the tagline. Strategic choices recorded: - Trilogy (alpha) over mega-release (beta) or split (gamma) - R2 (keep name, rebrand tagline) over R1 (keep everything) or R3 (rename, prohibitive cost) - Update UX: option A — passive notify + manual install, zero telemetry, ENGRAM_NO_UPDATE_CHECK + \$CI opt-out Grounded in measured research: - npm downloads 1.3K/week, 10/day organic baseline - r/LocalLLaMA post ratioed (0.44) due to name collision with 4 other "Engram" projects launched Mar-Apr 2026 - Serena just hit stable with published evals — credible complement - 2 active external contributors writing substantive PRs Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NickCirv
added a commit
that referenced
this pull request
Apr 21, 2026
Adds a strict parser for file-mutating Bash commands — rm, mv, cp,
git rm, git mv, and single-redirect commands. Returns a list of
FileOp { action: "reindex" | "prune", path: absolute } that the
PostToolUse dispatch path will hand to syncFile() from watcher.ts.
This is HALF of issue #14. The other half is the dispatch wire-up
(route PostToolUse:Bash through this parser and invoke syncFile for
each op) which depends on PR #13's --auto-reindex flag landing first.
The parser ships standalone so it is review-ready and test-complete
before #13 merges.
Parser philosophy (mirrors handlers/bash.ts PreToolUse):
STRICT. Any ambiguity → passthrough. False negatives cost tokens;
false positives corrupt the graph. Optimize for the latter.
Supported shapes (intercepted):
rm [-rf] <path> [<path>...] → prune each
mv <src> <dst> → prune src, reindex dst
cp <src> <dst> → reindex dst
git rm [-r] <path> → prune
git mv <src> <dst> → prune src, reindex dst
<simple-cmd> > <dst> → reindex dst (single redirect)
<simple-cmd> >> <dst> → reindex dst (single redirect)
Intentionally NOT supported (pass through):
- globs (rm *.ts)
- pipes (find . | xargs rm)
- subshells / command substitution (\$(…), backticks, <(…), >(…))
- touch (empty file, nothing to index)
- directory-level ops (needs directory-prefix prune primitive,
tracked for v2.2)
Tests: 29 new tests across rm/mv/cp/git-rm/git-mv variants,
redirections, pass-through edge cases, and the high-level
handleBashPostTool wrapper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NickCirv
added a commit
that referenced
this pull request
Apr 21, 2026
…h-postool ## Closes #11 — AST/LSP reported unavailable despite enabled Credit: Tommaso Tessarolo (@ttessarolo) — precise forensics + suggested fix. ### checkAst: flattened-bundle path resolution When tsup/esbuild flattens the CLI into `engramx/dist/chunk-*.js`, import.meta.url resolves `here = engramx/dist`. The previous candidates: - `here + '../grammars'` → engramx/grammars (✗ does not exist) - `here + '../../dist/grammars'` → parent/dist/grammars (✗ does not exist) Fix: add `join(here, "grammars")` as the FIRST candidate. This resolves to `engramx/dist/grammars/` where grammars actually ship. Dev-time layout (src/intercept/) still works via the third candidate. ### checkLsp: socket candidates out of sync The check only looked for `tsserver.sock` and `typescript-language-server.sock`, but `lsp-connection.ts::candidateSockets()` also probes: - tsserver-<uid>.sock - lsp-server.sock - pyright-<uid>.sock - rust-analyzer.sock Also: the `.engram/lsp-available` flag was documented as "written by the lsp provider on successful connection" but no code path writes it. Kept the marker as an explicit user opt-in (back-compat) but synced the socket list with lsp-connection.ts so HUD availability matches actual provider availability. ### Regression test tests/intercept/component-status-11.test.ts — asserts refreshComponentStatus returns a boolean for every scenario + honors the .engram/lsp-available marker. ## Wires bash-postool into PostToolUse (issue #14 full wire-up) With @gabiudrescu's PR #12 already merged, syncFile() is available. We can wire the v2.1 Bash parser into the PostToolUse observer path without waiting for PR #13's install-hook --auto-reindex flag. - post-tool.ts imports handleBashPostTool + syncFile. - On PostToolUse with tool=Bash, parse the command; for each FileOp, call syncFile(absPath, projectRoot) fire-and-forget. - Gated by ENGRAM_AUTO_REINDEX=1 (opt-in) until PR #13 lands and we can migrate to the install-hook flag. - Observer semantics preserved: never blocks the PostToolUse response, errors swallowed, never surfaces bugs to Claude Code. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NickCirv
added a commit
that referenced
this pull request
Apr 24, 2026
Follow-up to cherry-picks b4f7944 (PR #6 commit 1) + 411ad13 (PR #6 commit 2) from @mechtar-ru. Deletes redundant DEFAULT_EXCLUDED_DIRS (15 entries) + loadEngramIgnore() (16 lines) that lived in parallel with the canonical DEFAULT_SKIP_DIRS + loadIgnorePatterns() pair (the latter shipped in v2.1.0 via PR #13). Both pairs implemented the same .engramignore feature — keeping only the v2.1 canonical pair keeps one source of truth. Also tightens entries typing: 'let entries: Dirent[]' in extractDirectory (ReturnType<typeof readdirSync> resolves to the string[] default overload, not the Dirent[] shape actually returned with { withFileTypes: true }). All 784 tests pass. TypeScript clean. Closes issue #5 (via PR #6 content: MAX_DEPTH=100 + MAX_FILES_PER_COMMIT=50 + .engramignore support + expanded default skip dirs — the OOM crash on init for 2.2GB/34K-file projects like Axolotl is fixed).
shahe-dev
pushed a commit
to shahe-dev/engram
that referenced
this pull request
Apr 25, 2026
First release in the v2.1 / v2.2 / v3.0 elevation trilogy. Ships: - engram update / doctor / setup — zero-friction install + self-upgrade - engram init --with-hook — one-command init + hook install - First-run hint on stderr (throttled, \$CI-safe, opt-out env) - Bash PostTool parser for auto-reindex (issue NickCirv#14 half, opt-in via ENGRAM_AUTO_REINDEX=1 until PR NickCirv#13's --auto-reindex flag becomes authoritative) - Fix issue NickCirv#11 — AST grammar detection in flattened bundles (@ttessarolo) + LSP socket candidate sync - ESM require() runtime bug fix across doctor/setup (would have silently failed in production; caught by in-process audit, not by CI because vitest provides CommonJS interop) - Windows cross-platform test fix for bash-postool (platform-native path.resolve in expected values, not hard-coded POSIX) See CHANGELOG.md [2.1.0] for full detail + contributor credits. Release artifacts: - package.json version 2.0.2 → 2.1.0 - package-lock.json synced - CHANGELOG [Unreleased] entries rehomed under [2.1.0] — 2026-04-21 Post-merge steps (not in this commit): git tag -a v2.1.0 -m "v2.1.0 Reliability + Zero-Friction Install" git push --tags npm publish gh release create v2.1.0 --generate-notes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #8.
Decisions you might push back on
files exit silently; your acceptance note says
missing-but-previously-indexed should be "an explicit prune, not a
silent no-op". I split it: never-indexed missing file → silent;
previously-indexed now-missing →
engram: pruned <file> (<N> nodes).One-line change in
formatReindexLineto flip prune back to silentif you'd rather.
runReindexHookwalks from the FILE path, notcwd. Caughtthis live during smoke testing: a Claude Code session
cwdoftensits ABOVE the engram-initialized project that owns the edited file
(monorepo, multi-project parent).
findProjectRoot(cwd)silentno-ops there. Fixed, test covers the shape.
isEngramHookEntryhadto broaden to match
engram reindex-hookso uninstall cleans itup. But using the broadened check for install idempotence would
false-positive on an auto-reindex-only PostToolUse and skip adding
the missing intercept entry. Split it:
entryContainsCommand(entry, "engram intercept")for installtargeting;
isEngramHookEntrystays broad for uninstall.--quietskipped. I proposed it in Feature:engram reindex <file>subcommand + optional PostToolUse auto-reindex #8; you didn't accept orreject. The hook subcommand is silent by design and CLI output is
one line, so I left it out. Easy add if you want it.
next question — what about
rm foo.tsvia Bash? Out of scope here(matcher is
Edit|Write|MultiEditper your note), but I'll file aseparate issue proposing to widen to
Bashand parsetool_input.commandforrm/mv/git-rm/redirections.src/intercept/handlers/bash.tsis the precedent for that kind ofparsing.
Test plan
npm run lintcleannpm test -- --run— 704 passed (+16 new)npm run build:nogrammarscleaninstall-hook --auto-reindex→Writenew file →engram queryfinds new function → delete +engram reindexemits correct prune line → query empty →uninstall-hookremoves both engram entries, preserves non-engram.🤖 Generated with Claude Code